
#ifndef _TTG_PIPELINE_INTERNALCONTEXT_
#define _TTG_PIPELINE_INTERNALCONTEXT_
#include "Common/Definitions.h"

#include <vector>
#include "Common/Structures.h"
#include "Runtime.Low/Threads.h"
#include "Runtime.Low/Services.h"

#include "Informations/PerformerInfo.h"
#include "ContextMap.h"
#include "ISubPipeline.h"

namespace ttg_internal
{
	namespace pipeline
	{
		//Internal structure, that defines keeper of variables in the UserContext.
		class UserContextRecord :public ttg::structures::NamedObject
		{
			protected:
				virtual void *getRawRef() = 0;
			public:
				inline UserContextRecord(const char *name)
					:ttg::structures::NamedObject(name)
				{ /*nothing*/ }
				//Returns parametrized reference.
				//In case of enabled RTTI can throw ttg::Error().
				template <class T>
				T *getRef()
				{ return (T *)getRawRef(); }
				//For descendants only.
				virtual ~UserContextRecord()
				{ /*nothing*/ }
		};
		//Implements creation and safe destruction of required variable.
		template <class T>
		class UserContextParametrizedRecord :public UserContextRecord
		{
			private:
				ttg::structures::ReleasableWrapper<T> data;
			protected:
				virtual void *getRawRef()
				{ return (void *)&data.get(); }
			public:
				//Creates record for local variable.
				inline UserContextParametrizedRecord(const char *name)
					:UserContextRecord(name)
				{ data.set(new T(), true); }
				//Creates record for global variable.
				inline UserContextParametrizedRecord(const char *name, T *variable)
					:UserContextRecord(name)
				{ data.set(variable, false); }
		};
	}
}

namespace ttg
{
	namespace pipeline
	{
		//Arbitrary set of variables, that can be defined by user.
		//These variables will be viewable by all nodes.
		class UserContext
		{
			private:
				ttg::structures::Dictionary<ttg_internal::pipeline::UserContextRecord> *records;
				ttg::threads::FastCriticalSection locker;
			public:
				//Creates empty user context.
				inline UserContext()
				{ records = ttg::structures::Dictionary<ttg_internal::pipeline::UserContextRecord>::create(50); }
				//Registers existent variable with required name.
				template <class T>
				inline void registerVariable(const char *name, T *variable)
				{
					locker.enter();
					if (records->get(name) == NULL)
					{
						records->add(
							new ttg_internal::pipeline::UserContextParametrizedRecord<T>(name, variable),
							true);
					}
					locker.leave();
				}
				//Creates new variable with required name. Returns reference to it.
				//In case of  errors can throw ttg::Error().
				template <class T>
				inline T &createVariable(const char *name)
				{
					T *res;
					ttg_internal::pipeline::UserContextRecord *rec;
					locker.enter();
					if ((rec = records->get(name)) == NULL)
					{
						//Creating new record.
						rec = new ttg_internal::pipeline::UserContextParametrizedRecord<T>(name);
						records->add(rec, true);
					}
					res = rec->getRef<T>();
					locker.leave();
					return *res;
				}
				//Returns reference to existent variable with required name.
				//In case of  errors can throw ttg::Error().
				template <class T>
				inline T &getVariable(const char *name)
				{
					T *res;
					ttg_internal::pipeline::UserContextRecord *rec;
					locker.enter();
					if ((rec = records->get(name)) == NULL)
					{
						throw ttg::Error(ttg::Error::FATAL, "UserContex", "getVariable",
										 "Variable with required name was not registered/created", name);
					}
					res = rec->getRef<T>();
					locker.leave();
					return *res;
				}
				//Releases all created variables.
				~UserContext()
				{
					SAFE_DELETE(records);
				}
		};
		//Contains information about current pipeline.
		class PipelineContext
		{
			private:
				ttg::pipeline::ContextMap<structures::CastableObject *> input_data;
				ttg::structures::ReleasableWrapper<UserContext> user_context;
			public:
				//Sets new user context.
				//Note: it's internal method.
				inline void setUserContext(UserContext *user_context)
				{ this->user_context.set(user_context, false); }
				//Sets data.
				//Note: use "clearInputData" for internal objects deleleting.
				//Note: it's internal method.
				template <class InType>
				inline void setInputData(ttg::pipeline::ContextID ID,
										 ttg::structures::Enumerator<InType> &data)
				{
					input_data[ID]
						= ttg::structures::CastableObject::create<std::vector<InType>>(data.buildVector());
				}
				//Clears data, that was assigned to context.
				//Note: it's internal method.
				inline void clearInputData(ttg::pipeline::ContextID ID)
				{ SAFE_DELETE(input_data[ID]); }
			public:
				//Creates empty context for pipeline.
				inline PipelineContext()
				{ user_context.set(new UserContext(), true); }
				//Returns data, that was passed to current pipeline (or sub-pipeline).
				//In case of wrong type throws ttg::Error().
				template <class InType>
				inline std::vector<InType> &getInputData(ttg::pipeline::ContextID ID)
				{
					return input_data[ID]->cast<std::vector<InType>>();
				}
				//Returns variable with required name, that was defined in UserContext.
				//If there aren't such variable, creates new one.
				template <class T>
				inline T &getVariable(const char *name)
				{ return user_context.get().getVariable<T>(name); }
				//The Owners.
				template <class InType, class OutType>
				friend class Pipeline;
		};
	}
}


namespace ttg_internal
{
	namespace pipeline
	{
		//Definition of used class.
		class NavigatorNode;

		//Global buffer, in whcih data from sendNext()/sendThis() will be stored.
		//Splits incoming data by thread-owner.
		class DataBuffer
		{
			private:
				//in TTGTriple:
				//	first - for this,
				//	second - for next,
				//	third - ID of current context.
				ttg::threads::ThreadMap<TTGTriple<ttg::structures::UniversalStack,
												  ttg::structures::UniversalStack,
												  ttg::pipeline::ContextID>> data;
			public:
				//Returns buffer for data, that was produced by "sendThis()".
				ttg::structures::UniversalStack &getThisBuffer()
				{ return data.getValue().getFirst(); }
				//Returns buffer for data, that was produced by "sendNext()".
				ttg::structures::UniversalStack &getNextBuffer()
				{ return data.getValue().getSecond(); }
				//Returns ID of current context.
				inline ttg::pipeline::ContextID &getContextID()
				{ return data.getValue().getThird(); }
				//Should be called between pipeline iterations.
				inline void reBuild()
				{ data.reBuild(); }
		};

		//Contains internal information, which is common for all nodes of current pipeline/subsystem.
		//Can be extended by user context.
		//Note: there is only one instance of this class per pipeline.
		class InternalContext
		{
			private:
				//Enumeration of all nodes of pipeline (except subpipelines).
				std::vector<NavigatorNode *> *node_enumeration;
				//Essence, that receives new tasks.
				ttg::pipeline::ContextMap<
					ttg::services::ITaskScheduler<ttg::services::WiseTask *> *> task_gatherers;
				//Collection with all subpipelines.
				ttg::structures::Dictionary<ISubPipeline> *sub_pipelines;
				//Buffer for data exchanging between process(), sendNext() and sendThis().
				DataBuffer data_buffer;
				//Generator for context ID.
				ContextIDManager context_id_manager;
				//ID of root context (which will never be released).
				ttg::pipeline::ContextID root_context_id;
				//Contexts for user.
				ttg::pipeline::PipelineContext pipeline_context;
#ifdef TTG_PL_VISUALIZER
				ttg::threads::ThreadMap<PerformerInfo> performers;
#endif
			public:
				//Creates unconfigured context.
				InternalContext();
				//Returns enumerated nodes of current pipeline.
				//Note: it can throw ttg::Error().
				std::vector<NavigatorNode *> &getPipelineEnumeration();
				//Sets pipeline enumeration.
				inline void setPipelineEnumeration(std::vector<NavigatorNode *> *enumeration)
				{ node_enumeration = enumeration; }
				//Returns current gatherer, which is assigned to pipeline.
				//Note: it can throw ttg::Error().
				inline ttg::services::ITaskScheduler<ttg::services::WiseTask *> &getTaskGatherer(
					ttg::pipeline::ContextID ID)
				{ return *task_gatherers[ID]; }
				//Sets task gatherer for current pipeline.
				inline void setTaskGatherer(ttg::services::ITaskScheduler<ttg::services::WiseTask *> *gatherer,
											ttg::pipeline::ContextID ID)
				{ task_gatherers[ID] = gatherer; }
				//Sets collection with all known subpipelines.
				inline void setSubPipelines(ttg::structures::Dictionary<ISubPipeline> *sub_pipelines)
				{ this->sub_pipelines = sub_pipelines; }
				//Returns pipeline with required name (or NULL).
				ISubPipeline *getSubPipeline(const char *name);
				//Returns buffer for storing of produced data.
				inline DataBuffer &getDataBuffer()
				{ return data_buffer; }
				//Returns manager for context IDs.
				inline ContextIDManager &getContextIDManager()
				{ return context_id_manager; }
				//Return ID of root context (which always is the same).
				inline ttg::pipeline::ContextID getRootContextID()
				{ return root_context_id; }
				//Returns context for current launch.
				inline ttg::pipeline::PipelineContext &getPipelineContext()
				{ return pipeline_context; }
#ifdef TTG_PL_VISUALIZER
				//Returns information about performers.
				inline ttg::threads::ThreadMap<PerformerInfo> &getPerformers()
				{ return performers; }
#endif
		};
	}
}

#endif
